package edu.uc.labs.MobilePrinting.PrintUtils;

import edu.uc.labs.MobilePrinting.Fetch.PPDFetcher;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import org.apache.log4j.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import edu.uc.labs.MobilePrinting.Configurator;
import java.io.File;

import javax.xml.parsers.ParserConfigurationException;

/**
 * 
 * @author Matt Silbernagel
 */
public class PrinterInstaller {

	private String osName, cache_dir, winScriptsPath, delim, filename;
	final static String PORTNAME = Configurator.PORTNAME,
			PRINTERNAME = Configurator.PORTNAME;
	private Printer printer = null;
	private Configurator conf = new Configurator();
	private Logger logger = conf.getLogger();

	public PrinterInstaller(Printer prn) {
		printer = prn;
		osName = Configurator.OS;
		cache_dir = Configurator.CACHEDIR;
		delim = Configurator.DELIM;
		winScriptsPath = Configurator.WINSCRIPTSPATH;
		filename = cache_dir + "ppd" + delim + printer.getZone() + delim
				+ printer.getQueue() + ".ppd";

	}

	public boolean install() {
		if (osName.matches("Windows(.)*")) {
			try {
				logger.debug("You appear to using a windows machine");
				boolean uPort = winAddPort();
				if (uPort) {
					// Successfully updated/add the port
					// now checking for the printer
					if (!winCheckForPrinter()) {
						winAddPrinter();
						return true;
					}
					try {
						PrinterCache.writeCache(printer);
					} catch (ParserConfigurationException ex) {
						logger.error(ex);
					}
				}
			} catch (IOException ex) {
				logger.error(ex.toString());
				return false;
			}

		} else if (osName.matches("Linux") || osName.matches("SunOS")) {
			logger.debug("This is Linux");

			boolean check = checkForPPD("linux");
			if (check) {
				// The ppd is in place...
				logger.debug("The printer is being installed...");
				try {
					boolean res = installNixPrinter() ? true : false;
					try {
						PrinterCache.writeCache(printer);
					} catch (ParserConfigurationException ex) {
						logger.error(ex);
					}

					return res;
				} catch (IOException ex) {
					logger.error(ex.toString());
					return false;
				}
			} else {
				logger.debug("Could not find the PPD file...");
			}

		} else if (osName.startsWith("Mac OS X")) {
			logger.debug("This is Mac OS X");
			boolean check = checkForPPD("mac");
			if (check) {
				logger.debug("The printer is being installed...");
				try {
					installNixPrinter();
					try {
						PrinterCache.writeCache(printer);
					} catch (ParserConfigurationException ex) {
						logger.error(ex);
					}
					return true;
				} catch (IOException ex) {
					logger.error(ex.getMessage());
					return false;
				}
			} else {
				logger.debug("Could not find the PPD file...");
				return false;
			}
		}
		return false;

	}

	public boolean winAddPort() throws IOException {
		String line = null;
		String addPortcmd = "cscript.exe " + winScriptsPath
				+ "\\prnport.vbs -a -r " + PORTNAME + " -h "
				+ printer.getServer() + " -q " + printer.getQueue() + " -o lpr";

		System.out.println("Updating port... ");
		Process p = Runtime.getRuntime().exec(addPortcmd);
		boolean successful = false;
		// What does the output of the command report
		BufferedReader input = new BufferedReader(new InputStreamReader(p
				.getInputStream()));
		while ((line = input.readLine()) != null) {
			System.out.println(line);
			Pattern pattern = Pattern.compile("Created/updated port (.)*");
			Matcher mat = pattern.matcher(line);
			if (mat.find()) {
				successful = true;
			}
		}
		return successful;
	}

	private void winAddPrinter() throws IOException {
		String unc = "\\\\" + Configurator.HTTP_SERVER + Configurator.DELIM
				+ "Printers" + Configurator.DELIM + "HPUniversal"
				+ Configurator.DELIM + "hpcu094d.inf";
		String s = null;
		String[] insPrinter = { "rundll32", "printui.dll,PrintUIEntry", "/if",
				"/b", PRINTERNAME, "/f", unc, "/u", "/r", PORTNAME, "/m",
				"HP Universal Printing PS" };

		Process p = Runtime.getRuntime().exec(insPrinter);
		BufferedReader stdInput = new BufferedReader(new InputStreamReader(p
				.getInputStream()));

		BufferedReader stdError = new BufferedReader(new InputStreamReader(p
				.getErrorStream()));

		// read the output from the command
		logger.debug("Here is the standard output of the command:\n");

		while ((s = stdInput.readLine()) != null) {
			logger.debug(s);
		}

		// read any errors from the attempted command
		logger.debug("Here is the standard error of the command (if any):\n");
		while ((s = stdError.readLine()) != null) {
			logger.debug(s);
		}

	}

	private boolean winCheckForPrinter() throws IOException {
		boolean isInstalled = false;
		String line = null;
		String checkPrinter = "cscript.exe " + winScriptsPath
				+ "\\prnmngr.vbs -l";

		System.out.println("Checking for existence of " + PRINTERNAME + "...");
		Process p = Runtime.getRuntime().exec(checkPrinter);

		BufferedReader input = new BufferedReader(new InputStreamReader(p
				.getInputStream()));
		while ((line = input.readLine()) != null) {
			Pattern pattern = Pattern.compile("Printer name " + PRINTERNAME
					+ "(.)*");
			Matcher mat = pattern.matcher(line);
			if (mat.find()) {
				System.out.println("There is printer installed called "
						+ PRINTERNAME);
				isInstalled = true;
			}
		}
		return isInstalled;
	}

	private boolean checkForPPD(String os) {
		boolean doesExist = false;
		File f = new File(filename);
		for (int i = 0; i < 4; i++) {
			if (f.exists()) {
				// The ppd already exists, start the installation
				logger.debug("The ppd already exists...");
				doesExist = true;
				break;
			} else {
				logger.debug("The ppd does not exist yet...");
				PPDFetcher fetch = new PPDFetcher(Configurator.HTTP_SERVER,
						printer.getZone(), printer.getQueue(), os);
				fetch.writeToFile();
			}
		}
		return doesExist;
	}

	private boolean installNixPrinter() throws IOException {
		@SuppressWarnings("unused")
		String s = null;
		String[] insPrinter = { "/usr/sbin/lpadmin", "-p",
				Configurator.PRINTERNAME, "-o", "printer-is-shared=false",
				"-v",
				"lpd://" + printer.getServer() + "/" + printer.getQueue(),
				"-L", printer.getLocation(), "-D", Configurator.PRINTERNAME,
				"-P", filename, "-E" };
		Process p = Runtime.getRuntime().exec(insPrinter);

		BufferedReader stdInput = new BufferedReader(new InputStreamReader(p
				.getInputStream()));

		BufferedReader stdError = new BufferedReader(new InputStreamReader(p
				.getErrorStream()));

		// read the output from the command
		// System.out.println("Here is the standard output of the command:\n");
		while ((s = stdInput.readLine()) != null) {
			// System.out.println(s);
		}

		// read any errors from the attempted command
		// System.out.println("Here is the standard error of the command (if any):\n");
		if ((s = stdError.readLine()) != null) {
			return false;
		}
		return true;

	}

}
